﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using Model;
using Communicator;
using System.Threading;

namespace SoundConstructor
{
	public class Presenter
	{
		AudioPlayer audioPlayer = new AudioPlayer();
		Storage storage = new Storage();
		Communicator.Communicator communicator = new Communicator.Communicator();
		Imitator imitator = Imitator.Instance;
		View view;

		List<OnetimeSound> onetimeSounds;
		List<RepeatedSound> repeatedSounds;

		OnetimeSound currentOnetimeSound = new OnetimeSound();
		RepeatedSound currentRepeatedSound = new RepeatedSound();

		BackgroundWorker updateViewBackgroundWorker = new BackgroundWorker();
		bool imitationIsRunning;

		public Presenter(View view)
		{
			RestoreAllSounds();
			imitator.Communicator = communicator;
			imitator.AudioPlayer = audioPlayer;
			imitator.OnetimeSounds = onetimeSounds;
			imitator.RepeatedSounds = repeatedSounds;

			this.view = view;
			view.Presenter = this;
			view.NamesOfOnetimeSounds = NamesOfOnetimeSounds;
			view.NamesOfRepeatedSounds = NamesOfRepeatedSounds;

			updateViewBackgroundWorker.DoWork +=
				new DoWorkEventHandler(UpdateView);
			updateViewBackgroundWorker.WorkerSupportsCancellation
				= true;
			updateViewBackgroundWorker.WorkerReportsProgress = true;
			updateViewBackgroundWorker.ProgressChanged +=
				new ProgressChangedEventHandler(updateViewBackgroundWorker_ProgressChanged);
		}

		void updateViewBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			UpdateView();
		}

		void UpdateView(object sender, DoWorkEventArgs e)
		{
			while (true)
			{
				Thread.Sleep(50);
				updateViewBackgroundWorker.ReportProgress(0);
				if (updateViewBackgroundWorker.CancellationPending)
				{
					e.Cancel = true;
					return;
				}
			}
		}

		private void RestoreAllSounds()
		{
			storage.Restore();
			onetimeSounds = storage.GetOnetimeSounds();
			repeatedSounds = storage.GetRepeatedSounds();
		}

		public IList<string> NamesOfOnetimeSounds
		{
			get
			{
				IList<string> namesOfOnetimeSounds = new List<string>();
				foreach (OnetimeSound sound in onetimeSounds)
				{
					namesOfOnetimeSounds.Add(sound.Name);
				}
				return namesOfOnetimeSounds;
			}
		}

		public IList<string> NamesOfRepeatedSounds
		{
			get
			{
				IList<string> namesOfRepeatedSounds = new List<string>();
				foreach (RepeatedSound sound in repeatedSounds)
				{
					namesOfRepeatedSounds.Add(sound.Name);
				}
				return namesOfRepeatedSounds;
			}
		}

		public string NameOfOnetimeSound
		{
			get { return currentOnetimeSound.Name; }
			set
			{
				currentOnetimeSound.Name = value;
				UpdateView();
			}
		}

		public string XmlParameterName
		{
			get { return currentOnetimeSound.XmlParameterName; }
			set
			{
				currentOnetimeSound.XmlParameterName = value;
				UpdateView();
			}
		}

		public double XmlParameterValue
		{
			get { return currentOnetimeSound.XmlParameterValue; }
			set
			{
				currentOnetimeSound.XmlParameterValue = value;
				audioPlayer.SetVolume(currentOnetimeSound.Name, currentOnetimeSound.Volume);
				audioPlayer.SetFrequency(currentOnetimeSound.Name, currentOnetimeSound.Frequency);
				UpdateView();
			}
		}

		public uint MaxDuration
		{
			get { return currentOnetimeSound.MaxDuration; }
			set
			{
				currentOnetimeSound.MaxDuration = value;
				UpdateView();
			}
		}

		public bool BlockRepeat
		{
			get { return currentOnetimeSound.BlockRepeat; }
			set
			{
				currentOnetimeSound.BlockRepeat = value;
				UpdateView();
			}
		}

		public string SoundFileLocationOfOnetimeSound
		{
			get { return currentOnetimeSound.SoundFileLocation; }
			set
			{
				currentOnetimeSound.SoundFileLocation = value;
				audioPlayer.SetSoundFileLocation(currentOnetimeSound.Name,
					currentOnetimeSound.SoundFileLocation);
				UpdateView();
			}
		}

		public double SpecifiedVolume
		{
			get { return currentOnetimeSound.SpecifiedVolume; }
			set
			{
				currentOnetimeSound.SpecifiedVolume = value;
				audioPlayer.SetVolume(currentOnetimeSound.Name, currentOnetimeSound.Volume);
				UpdateView();
			}
		}

		public double SpecifiedFrequency
		{
			get { return currentOnetimeSound.SpecifiedFrequency; }
			set
			{
				currentOnetimeSound.SpecifiedFrequency = value;
				audioPlayer.SetFrequency(currentOnetimeSound.Name, currentOnetimeSound.Frequency);
				UpdateView();

			}
		}

		public string NameOfRepeatedSound
		{
			get { return currentRepeatedSound.Name; }
			set
			{
				currentRepeatedSound.Name = value;
				UpdateView();
			}
		}

		public string NameOfXmlParameterForVolume
		{
			get { return currentRepeatedSound.NameOfXmlParameterForVolume; }
			set
			{
				currentRepeatedSound.NameOfXmlParameterForVolume = value;
				UpdateView();
			}
		}

		public string NameOfXmlParameterForFrequency
		{
			get { return currentRepeatedSound.NameOfXmlParameterForFrequency; }
			set
			{
				currentRepeatedSound.NameOfXmlParameterForFrequency = value;
				UpdateView();
			}
		}

		public string NameOfXmlParameterForEcho
		{
			get { return currentRepeatedSound.NameOfXmlParameterForEcho; }
			set
			{
				currentRepeatedSound.NameOfXmlParameterForEcho = value;
				UpdateView();
			}
		}

		public double ValueOfXmlParameterForVolume
		{
			get { return currentRepeatedSound.ValueOfXmlParameterForVolume; }
			set
			{
				currentRepeatedSound.ValueOfXmlParameterForVolume = value;
				audioPlayer.SetVolume(currentRepeatedSound.Name,
					currentRepeatedSound.Volume);
				UpdateView();
			}
		}

		public double ValueOfXmlParameterForFrequency
		{
			get { return currentRepeatedSound.ValueOfXmlParameterForFrequency; }
			set
			{
				currentRepeatedSound.ValueOfXmlParameterForFrequency = value;
				audioPlayer.SetFrequency(currentRepeatedSound.Name,
					currentRepeatedSound.Frequency);
				UpdateView();
			}
		}

		public double ValueOfXmlParameterForEcho
		{
			get { return currentRepeatedSound.ValueOfXmlParameterForEcho; }
			set
			{
				currentRepeatedSound.ValueOfXmlParameterForEcho = value;
				audioPlayer.EnableEcho(currentRepeatedSound.Name,
					currentRepeatedSound.Echo);
				UpdateView();
			}
		}

		public double VolumeOfRepeatedSound
		{
			get { return currentRepeatedSound.Volume; }
		}

		public double FrequencyOfRepeatedSound
		{
			get { return currentRepeatedSound.Frequency; }
		}

		public string SoundFileLocationOfRepeatedSound
		{
			get { return currentRepeatedSound.SoundFileLocation; }
			set
			{
				currentRepeatedSound.SoundFileLocation = value;
				audioPlayer.SetSoundFileLocation(currentRepeatedSound.Name,
					currentRepeatedSound.SoundFileLocation);
				UpdateView();
			}
		}

		public SortedList<double, double> TableOfValuesForVolume
		{
			get { return currentRepeatedSound.TableOfValuesForVolume; }
			set
			{
				currentRepeatedSound.TableOfValuesForVolume = value;
				UpdateView();
			}
		}

		public SortedList<double, double> TableOfValuesForFrequency
		{
			get { return currentRepeatedSound.TableOfValuesForFrequency; }
			set
			{
				currentRepeatedSound.TableOfValuesForFrequency = value;
				UpdateView();
			}
		}

		public bool ImitationIsRunning
		{
			get { return imitationIsRunning; }
			set
			{
				imitationIsRunning = value;
				if (imitationIsRunning)
				{
					imitator.StartImitation();
					updateViewBackgroundWorker.RunWorkerAsync();
				}
				else
				{
					imitator.StopImitation();
					updateViewBackgroundWorker.CancelAsync();
				}
				UpdateView();
			}
		}

		internal void UpdateView()
		{
			view.NameOfOnetimeSound = NameOfOnetimeSound;
			view.XmlParameterName = XmlParameterName;
			view.XmlParameterValue = XmlParameterValue;
			view.MaxDuration = MaxDuration;
			view.BlockRepeat = BlockRepeat;
			view.SoundFileLocationOfOnetimeSound = SoundFileLocationOfOnetimeSound;
			view.SpecifiedVolume = SpecifiedVolume;
			view.SpecifiedFrequency = SpecifiedFrequency;

			view.NameOfRepeatedSound = NameOfRepeatedSound;
			view.NameOfXmlParameterForVolume = NameOfXmlParameterForVolume;
			view.NameOfXmlParameterForFrequency = NameOfXmlParameterForFrequency;
			//view.NameOfXmlParameterForEcho = NameOfXmlParameterForEcho;
			view.ValueOfXmlParameterForVolume = ValueOfXmlParameterForVolume;
			view.ValueOfXmlParameterForFrequency = ValueOfXmlParameterForFrequency;
			//view.ValueOfXmlParameterForEcho = ValueOfXmlParameterForEcho;
			view.VolumeOfRepeatedSound = VolumeOfRepeatedSound;
			view.FrequencyOfRepeatedSound = FrequencyOfRepeatedSound;
			view.SoundFileLocationOfRepeatedSound = SoundFileLocationOfRepeatedSound;
			view.TableOfValuesForVolume = TableOfValuesForVolume;
			view.TableOfValuesForFrequency = TableOfValuesForFrequency;

			view.ImitationIsRunning = ImitationIsRunning;
		}

		internal void OpenSound(string soundName)
		{
			if (IsNameOfOnetimeSound(soundName))
			{
				currentOnetimeSound = (OnetimeSound)GetSoundByName(soundName);
			}
			else if (IsNameOfRepeatedSound(soundName))
			{
				currentRepeatedSound = (RepeatedSound)GetSoundByName(soundName);
			}

			UpdateView();
		}

		private object GetSoundByName(string soundName)
		{
			object sound = null;
			foreach (OnetimeSound aSound in onetimeSounds)
			{
				if (aSound.Name == soundName)
				{
					sound = aSound;
				}
			}

			foreach (RepeatedSound aSound in repeatedSounds)
			{
				if (aSound.Name == soundName)
				{
					sound = aSound;
				}
			}

			return sound;
		}

		private bool IsNameOfOnetimeSound(string soundName)
		{
			if (NamesOfOnetimeSounds.Contains(soundName))
			{
				return true;
			}
			else return false;
		}

		private bool IsNameOfRepeatedSound(string soundName)
		{
			if (NamesOfRepeatedSounds.Contains(soundName))
			{
				return true;
			}
			else return false;
		}

		internal void AddNewOnetimeSound()
		{
			AddNewOnetimeSoundCommand command =
				new AddNewOnetimeSoundCommand(currentOnetimeSound, onetimeSounds);
			command.Execute();

			view.NamesOfOnetimeSounds = NamesOfOnetimeSounds;
		}

		internal void AddRepeatedSound()
		{
			AddNewRepeatedSoundCommand command =
				new AddNewRepeatedSoundCommand(currentRepeatedSound, repeatedSounds);
			command.Execute();

			view.NamesOfRepeatedSounds = NamesOfRepeatedSounds;
		}

		internal void ChangeName(string oldNameOfSound, string newNameOfSound)
		{
			ChangeNameCommand command =
				new ChangeNameCommand(oldNameOfSound, newNameOfSound, onetimeSounds, repeatedSounds);
			command.Execute();
			UpdateView();
		}

		internal void PlayOnetimeSound(string soundName)
		{
			OnetimeSound sound = (OnetimeSound)GetSoundByName(soundName);
			audioPlayer.Play(sound.Name, sound.MaxDuration, sound.BlockRepeat,
				sound.Volume, sound.Frequency, sound.SoundFileLocation);
		}

		internal void PlayRepeatedSound(string soundName)
		{
			RepeatedSound sound = (RepeatedSound)GetSoundByName(soundName);
			audioPlayer.Play(sound.Name, sound.Volume, sound.Frequency,
				sound.Echo, sound.SoundFileLocation);
		}

		internal void PauseSound(string soundName)
		{
			audioPlayer.Pause(soundName);
		}

		internal void StopSound(string soundName)
		{
			audioPlayer.Stop(soundName);
		}

		internal void SaveAllSounds()
		{
			storage.SaveAllSounds(onetimeSounds, repeatedSounds);
		}

		internal void DeleteSound(string soundName)
		{
			DeleteSoundCommand command =
				new DeleteSoundCommand(soundName, onetimeSounds, repeatedSounds);
			command.Execute();
			UpdateView();
		}
	}
}
